import { BusyIndicator } from "../component/BusyIndicator";
import Logger from "../utils/Logger";
import { delay } from "../utils/utils";
import { HeartBeat } from "./HeartBeat";

const throttledFunctionMap = new Map<string, (messages: GameMessage | GameMessage[]) => Promise<void>>();
const MAX_RETRY_TIMES = 10;
const RETRY_DELAY_SECONDS = 0.125;
let gameServerUrl = "";

const messageHandlers = new Map<string, Function[]>();

export interface GameMessage {
    readonly messageName: string;
    rejectReason?: string;
    [fieldName: string]: any;
}

export interface GameClientMessageContainer {
    characterId: string;
    sessionId: string;
    serialNumber: number;
    messages: GameMessage[];

    /**
     * IP地址，发送时不需要带上
     */
    ip?: string;
}

export interface GameServerMessageContainer {
    sessionId?: string;
    serialNumber?: number;
    nowDateTime: Date;
    messages?: GameMessage[];
    error?: any;
}

export class SessionService {
    private static _currentSession: SessionService;

    userId!: string;
    sessionId!: string;
    portOffset = 0;
    serverIndex = 0;
    characterId!: string;
    serialNumber = 0;
    unsentMessages: GameMessage[] = [];
    sendingInProgress = false;

    static get currentSession() {
        if (!SessionService._currentSession) {
            SessionService._currentSession = new SessionService();
        }

        return SessionService._currentSession;
    }

    static resetSession() {
        SessionService._currentSession = new SessionService();
    }
}

export const getCurrentSession = () => SessionService.currentSession;
export const resetSession = SessionService.resetSession;

export class AxiosManager {
    static sendMessage = (messages: GameMessage | GameMessage[]) => {
        if (!getCurrentSession().sessionId) {
            return;
        }

        if (!messages) {
            return;
        }

        let sign = "";

        if (Array.isArray(messages)) {
            messages.forEach((message) => {
                if (sign.length > 0) {
                    sign += "-" + message.messageName;
                } else {
                    sign = message.messageName;
                }
            });
        } else {
            sign = messages.messageName;
        }

        let func: (messages: GameMessage | GameMessage[]) => Promise<void>;

        if (throttledFunctionMap.has(sign)) {
            func = throttledFunctionMap.get(sign)!;
        } else {
            let wait = 750; // 消息阀值（相同消息间隔<.75忽略）
            func = _.throttle(AxiosManager.sendThrottledMessage, wait, { trailing: false });
            throttledFunctionMap.set(sign, func);
        }

        func(messages);
    };

    private static sendThrottledMessage = async (messages?: GameMessage | GameMessage[]) => {
        let currentSession = getCurrentSession();

        if (!currentSession.sessionId) {
            return;
        }

        // 深拷贝
        if (messages) {
            messages = _.cloneDeep(messages);

            if (Array.isArray(messages)) {
                currentSession.unsentMessages.push(...messages);
            } else {
                currentSession.unsentMessages.push(messages!);
            }

            if (currentSession.sendingInProgress) {
                return;
            }

            // 先等等，等其它业务逻辑的消息合并在一起后发 (同时多个请求的情况)
            await delay(0.02);
        }

        if (currentSession.sendingInProgress || !currentSession.unsentMessages.length) {
            if (!messages) {
                AxiosManager.setBusyState(false);
            }

            return;
        }

        currentSession.sendingInProgress = true;

        const clientMessageContainer: GameClientMessageContainer = {
            characterId: currentSession.characterId,
            sessionId: currentSession.sessionId,
            serialNumber: currentSession.serialNumber++,
            messages: currentSession.unsentMessages,
        };

        currentSession.unsentMessages = [];
        Logger.log("AxiosManager", clientMessageContainer);
        let retryTimes = 0;
        let messageContainer: GameServerMessageContainer;
        AxiosManager.setBusyState(true);

        while (true) {
            try {
                const ret = await axios({
                    method: "POST",
                    url: `${gameServerUrl}`,
                    headers: {
                        ["Content-Type"]: "application/x-gamemsg",
                        Accept: "application/x-gamemsg, application/octet-stream, application/*",
                    },
                    responseType: "arraybuffer" as ResponseType,
                    // transformRequest: msgEncoder,
                    // transformResponse: msgDecoder,
                    data: clientMessageContainer,
                    timeout: 10000 + retryTimes * 500,
                });

                if (!ret || !ret.data) {
                    throw new Error("收到空消息。");
                }

                messageContainer = ret.data;
                break;
            } catch (err: any) {
                if (++retryTimes <= MAX_RETRY_TIMES) {
                    Logger.error(`发送消息第${retryTimes}次失败。`);
                    await delay(Math.min(10, RETRY_DELAY_SECONDS * Math.pow(2, retryTimes)));
                } else {
                    Logger.error(`发送消息彻底失败，共计尝试${retryTimes}次。`);
                    // 一旦失败，取消队列里所有的消息
                    currentSession.unsentMessages = [];
                    currentSession.sendingInProgress = false;

                    AxiosManager.setBusyState(false);

                    if (AxiosManager.onSendMessageFailure) {
                        AxiosManager.onSendMessageFailure(err);
                    }
                    return;
                }
            }
        }

        Logger.log("收", messageContainer);

        // 在这里一定要重新获取会话状态
        currentSession = getCurrentSession();

        // sessionId 为空代表着接下来的消息走长连接
        if (messageContainer.sessionId && messageContainer.sessionId !== currentSession.sessionId) {
            AxiosManager.setBusyState(false);
            Logger.error(`${messageContainer.sessionId} 已失效。`);
            return;
        }

        currentSession.sendingInProgress = false;

        if (currentSession.unsentMessages.length) {
            AxiosManager.sendThrottledMessage();
        } else {
            AxiosManager.setBusyState(false);
        }

        // sessionId 为空代表着接下来的消息走长连接
        if (messageContainer.sessionId && AxiosManager.onSendMessageSuccess) {
            AxiosManager.onSendMessageSuccess(messageContainer);

            // 从第三条消息才开始启用心跳
            if (2 === messageContainer.serialNumber) {
                HeartBeat.enableHeartbeat();
            }
        }
    };

    private static registerMessage(messageName: string, handler: Function) {
        const handlers = messageHandlers.get(messageName);
        if (!handlers) {
            messageHandlers.set(messageName, [handler]);
        } else {
            handlers.push(handler);
        }
    }

    private static onSendMessageSuccess(messageContainer: GameServerMessageContainer) {
        if (!messageContainer.messages) {
            return;
        }

        Logger.log("AxiosManager >> ", JSON.parse(JSON.stringify(messageContainer.messages)));
        messageContainer.messages.forEach((message: GameMessage) => {
            // if (message.error) {
            //     this.notify(MSG.RESPONSE.ERROR, message.messageName, message.error);
            //     return;
            // } else if (message.rejectReason) {
            //     this.notify(MSG.RESPONSE.ERROR, message.messageName, message.rejectReason);
            // } else if (message.description) {
            //     this.notify(MSG.RESPONSE.DESCRIPTION, message.messageName, message.description);
            // }

            const handlers = messageHandlers.get(message.messageName);
            try {
                if (handlers !== undefined && handlers.length > 0) {
                    handlers.forEach((func: Function) => {
                        func(message);
                    });
                }
            } catch (e) {
                Logger.error("message catch error:", e);
            }
        }, this);
    }

    private static onSendMessageFailure(err: any) {
        Logger.error("AxiosManager" + err);
    }

    private static setBusyState(isBusy: boolean): void {
        BusyIndicator.instance.setIsBusy(isBusy);
    }
}
